package com.nx.platform.es.biz.esspider.handler;

import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.nx.platform.es.common.utils.MoreSplitters;
import com.nx.platform.es.common.utils.Utils;
import com.nx.platform.es.biz.esspider.entity.Item;
import com.nx.platform.es.common.utils.MoreMaps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author wangjianying
 * @since 2019年8月20日
 */
@HandlerDefine(HandlerType.SIMPLE)
public class SimpleComplexValuesHandler extends AbstractSimpleHandler {

    private static final Type PARAMS_TYPE = new TypeToken<List<Param>>() {
    }.getType();
    private static final Type SERVICES_TYPE = new TypeToken<List<Service>>() {
    }.getType();
    private static final Logger LOGGER = LogManager.getLogger(SimpleComplexValuesHandler.class);
    private static final Gson GSON = new GsonBuilder().create();
    private static List<String> longParamIds;
    private static List<String> paramIds;
    private List<ComplexItem> items;

    @Override
    public void init(Map<?, ?> settings) throws Exception {
        longParamIds = MoreMaps.getStringList(settings, "longParamIds", MoreSplitters.COMMA);
        paramIds = MoreMaps.getStringList(settings, "paramIds", MoreSplitters.COMMA);
        items = ListUtils.emptyIfNull(MoreMaps.getStringList(settings, "items", MoreSplitters.COMMA))
                .stream().map(ComplexItem::valueOf).collect(Collectors.toList());
    }

    @Override
    public void handle(Map<Long, Item> items) throws Exception {
        items.values().forEach((Item item) -> {
            Map<String, Object> doc = item.getDoc();
            this.items.forEach(field -> {
                Object orig = doc.remove(field.key);
                ComplexItem item1 = ComplexItem.valueOf(field.toString());
                doc.putIfAbsent(field.alias, item1.convert(orig));
            });
        });
    }

    private enum ComplexItem {
        Param("params", "param") {
            @Override
            List<Long> convert(Object object) {
                if (object instanceof String) {
                    try {
                        List<Param> params = GSON.fromJson(String.valueOf(object), PARAMS_TYPE);
                        if (params != null) {
                            Map<String, List<Long>> map = new HashMap<>(params.size());
                            for (Param param : params) {
                                if (param.getParamId() != null && param.getValueId() != null) {
                                    List<Long> list = MoreSplitters.VERTICAL_OR_COMMA.splitAsStream(param.getValueId())
                                            .map(Longs::tryParse).filter(Objects::nonNull).filter(l -> l >= 0)
                                            .collect(Collectors.toList());
                                    if (!list.isEmpty()) {
                                        map.put(String.valueOf(param.getParamId()), list);
                                    }
                                }
                            }
                            return map.entrySet().stream()
                                    .filter(e -> CollectionUtils.isEmpty(paramIds) || paramIds.contains(e.getKey()))
                                    .flatMap(e -> {
                                        Integer key = Ints.tryParse(e.getKey());
                                        if (key == null) {
                                            return Stream.empty();
                                        }
                                        // merge只支持Integer
                                        return e.getValue().stream().filter(v -> v <= Integer.MAX_VALUE).map(v -> Utils.merge(key, v.intValue()));
                                    }).collect(Collectors.toList());
                        }
                    } catch (Throwable e) {
                        LOGGER.warn("convert params error: {}", object, e);
                    }
                }
                return Collections.emptyList();
            }
        },
        ParamLong("params", "paramlong") {
            @Override
            List<String> convert(Object object) {
                if (object instanceof String) {
                    try {
                        List<Param> params = GSON.fromJson(String.valueOf(object), PARAMS_TYPE);
                        if (params != null) {
                            Map<String, List<Long>> map = new HashMap<>(params.size());
                            for (Param param : params) {
                                if (param.getParamId() != null && param.getValueId() != null) {
                                    List<Long> list = MoreSplitters.VERTICAL_OR_COMMA.splitAsStream(param.getValueId())
                                            .map(Longs::tryParse).filter(Objects::nonNull).filter(l -> l >= 0)
                                            .collect(Collectors.toList());
                                    if (!list.isEmpty()) {
                                        map.put(String.valueOf(param.getParamId()), list);
                                    }
                                }
                            }
                            return map.entrySet().stream()
                                    .filter(entry -> !CollectionUtils.isEmpty(paramIds) && longParamIds.contains(entry.getKey()))
                                    .flatMap(e -> {
                                        Integer key = Ints.tryParse(e.getKey());
                                        if (key == null) {
                                            return Stream.empty();
                                        }
                                        // 针对long型数据处理
                                        return e.getValue().stream().map(v -> Utils.merge(key, v));
                                    }).collect(Collectors.toList());
                        }
                    } catch (Throwable e) {
                        LOGGER.warn("convert params error: {}", object, e);
                    }
                }
                return Collections.emptyList();
            }
        },
        Services("services") {
            @Override
            Set<Long> convert(Object object) {
                if (object instanceof String) {
                    try {
                        List<Service> services = GSON.fromJson(String.valueOf(object), SERVICES_TYPE);
                        if (services != null) {
                            Set<Long> set = new HashSet<>(services.size());
                            for (Service service : services) {
                                if (service.getServiceId() != null) {
                                    set.add(service.getServiceId());
                                }
                            }
                            return set;
                        }
                    } catch (Throwable e) {
                        LOGGER.warn("convert services error: {}", object, e);
                    }
                }
                return new HashSet<>(0);
            }
        };

        final String key;
        final String alias;

        abstract Object convert(Object object);

        ComplexItem(String key) {
            this.key = key;
            this.alias = key;
        }

        ComplexItem(String key, String alias) {
            this.key = key;
            this.alias = alias;
        }
    }

    public static class Param {

        private Integer paramId;
        private String valueId;

        public Integer getParamId() {
            return paramId;
        }

        public void setParamId(Integer paramId) {
            this.paramId = paramId;
        }

        public String getValueId() {
            return valueId;
        }

        public void setValueId(String valueId) {
            this.valueId = valueId;
        }

    }

    public static class Service {

        private Long serviceId;

        public Long getServiceId() {
            return serviceId;
        }

        public void setServiceId(Long serviceId) {
            this.serviceId = serviceId;
        }

    }

}
